package WebrtcWebsocketDemo;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

@ServerEndpoint(value = "/signal")
public class SignalServerEndpoint {

    private static Logger log = Logger.getLogger(SignalServerEndpoint.class.getName());

	private static final String GUEST_PREFIX = "Guest";
	private static final AtomicInteger connectionIds = new AtomicInteger(0);
	private static final Set<User> onlineUsers = new CopyOnWriteArraySet<User>();

	User user;

	public SignalServerEndpoint() {
		user = new User();
		user.id = connectionIds.getAndIncrement();
		user.name = GUEST_PREFIX + user.id;
		user.candidates = new ArrayList<String>();
	}

	@OnOpen
	public void start(Session session) {
		user.session = session;

		try {
			log.info(user.name + " joined!");
			user.session.getBasicRemote().sendText(
					String.format("Welcome, %s,%s!", user.name, user.session.getId()));
		} catch (IOException e) {
			e.printStackTrace();
		}
		// String message = String.format("* %s %s", nickname, "has joined.");
		// broadcast(message);
	}

	@OnClose
	public void end() {
		log.info("WebSocket closing");
		onlineUsers.remove(user);
		// String message = String
		// .format("* %s %s", nickname, "has disconnected.");
		// broadcast(message);
	}

	@OnMessage
	public void echoTextMessage(Session session, String msg, boolean last) {
		try {
			if (session.isOpen()) {
				JSONObject json = (JSONObject) JSONValue.parse(msg);
				if (json.containsKey("makehost")) {
					user.role = 1;
					onlineUsers.add(user);
					log.info("makehost=" + user.candidates.size());
					broadcast(user.name + " as Host Joined!");
				}
				if (json.containsKey("makeguest")) {
					if(onlineUsers.size() >= 2){
						session.getBasicRemote().sendText("Too many person!");
					}
					user.role = 2;
					onlineUsers.add(user);
					log.info("makeguest=" + user.candidates.size());
					broadcast(user.name + " as guest Joined!");
				}
				if (json.containsKey("sdp")) {
					user.sessionDescription = json.get("sdp").toString();
					log.info("SDP" + user.sessionDescription);
				}
				if (json.containsKey("candidate")) {
					user.candidates.add(json.get("candidate").toString());
					log.info("Candidates=" + user.candidates.size());
				}
				User another = getAnother();
				// send another msg to me 
				if(another != null){
					if(another.sessionDescription != null && another.sessionDescription != ""){
						JSONObject sdpJson = new JSONObject();
						sdpJson.put("sdp", another.sessionDescription);
						session.getBasicRemote().sendText(sdpJson.toString());
						another.sessionDescription = "";
					}
					
					for(String s : another.candidates){
						JSONObject tmpJson = new JSONObject();
						tmpJson.put("candidate", s);
						session.getBasicRemote().sendText(tmpJson.toString());
					}
					another.candidates.clear();
				}
				// send my msg to another
				if(another != null)
				{
					if(user.sessionDescription != null && user.sessionDescription != ""){
						JSONObject sdpJson = new JSONObject();
						sdpJson.put("sdp", user.sessionDescription);
						another.session.getBasicRemote().sendText(sdpJson.toString());
						user.sessionDescription = "";
					}
					
					for(String s : user.candidates){
						JSONObject tmpJson = new JSONObject();
						tmpJson.put("candidate", s);
						another.session.getBasicRemote().sendText(tmpJson.toString());
					}
					user.candidates.clear();
				}
			}
		} catch (Exception e) {
			try {
				log.log(Level.SEVERE, "Err on MSG," + e);
				session.close();
			} catch (IOException e1) {
				// Ignore
			}
		}
	}
	
	User getHost(){
		for(User u : onlineUsers){
			if(u.role == 1)
				return u;
		}
		return null;
	}
	
	User getGuest(){
		for(User u : onlineUsers){
			if(u.role == 2)
				return u;
		}
		return null;
	}
	
	User getAnother(){
		if(user.role==1)
			return getGuest();
		if(user.role==2)
			return getHost();
		return null;
	}

	@OnMessage
	public void echoBinaryMessage(Session session, ByteBuffer bb, boolean last) {
		try {
			if (session.isOpen()) {
				//log.log(Level.FINE, bb);
				session.getBasicRemote().sendBinary(bb, last);
			}
		} catch (IOException e) {
			try {
				log.log(Level.SEVERE, "ERR on bin msg="+e);
				session.close();
			} catch (IOException e1) {
				// Ignore
			}
		}
	}

	// @OnMessage
	// public void incoming(String message) {
	// // Never trust the client
	// String filteredMessage = String.format("%s: %s", nickname,
	// HTMLFilter.filter(message.toString()));
	// broadcast(filteredMessage);
	// }

//	@OnError
//	public void onError(Throwable t) throws Throwable {
//		log.error("Chat Error: " + t.toString(), t);
//	}
	
	@OnError
	public void onError(Throwable t) {
		log.log(Level.SEVERE, "Chat Error: " + t.toString());
	}

	private static void broadcast(String msg) {
		for (User u : onlineUsers) {
			try {
				synchronized (u) {
					u.session.getBasicRemote().sendText(msg);
				}
			} catch (IOException e) {
				log.log(Level.SEVERE, "Chat Error: Failed to send message to client", e);
				onlineUsers.remove(u);
				try {
					u.session.close();
				} catch (IOException e1) {
					// Ignore
				}
				String message = String.format("* %s %s", u.name,
						"has been disconnected.");
				broadcast(message);
			}
		}
	}
}